/*
	Copyright (C) 2011 Salil Bhagurkar

	This file is part of illusion

	illusion is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as
	published by the Free Software Foundation, either version 3 of
	the License, or (at your option) any later version.

	illusion is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with illusion. 
	If not, see <http://www.gnu.org/licenses/>.
*/

/*
 * Central kernel event management.
 *
 */

#include <klib/lib.h>
#include <klib/string.h>
#include <kernel/console.h>
#include <kernel/kmalloc.h>
#include <kernel/event.h>
#include <kernel/sched.h>
#include <kernel/list.h>

static struct event *events, *events_tail;


void event_register(struct event *e)
{
	list_attach(events, events_tail, e);
}

void event_unregister(struct event *e)
{
	list_detach(events, events_tail, e);
}

struct event *event_create(char *name)
{
	struct event *e = (struct event *)kmalloc(sizeof(struct event), "evt");
	strcpy(e->name, name, EVENT_NAME_LEN);
	e->waits = e->waits_tail = null;
	e->raise_data = null;
	return e;
}

void event_free(struct event *e)
{
	//Wake the waiting processes
	e->raise_data = null;
	e->raise_reason = EVENT_REASON_DEINIT;
	sched_wake_waitlist(&e->waits, &e->waits_tail);
	kfree(e, sizeof(struct event), "evt");
}

//TODO When a process is deleted, it must be dequeued from the waitlist also

void event_raise(struct event *e, void *data)
{
	e->raise_data = data;
	e->raise_reason = EVENT_REASON_EVENT;
	sched_wake_waitlist(&e->waits, &e->waits_tail);
}

void event_wait(void **data, struct event *e)
{
	//Register on the event and wait to be woken up
	sched_wait_process(&e->waits, &e->waits_tail, current_process);
	//Sleep
	sched_release();
	//Collect data and return
	*data = e->raise_data;
}

struct event *event_get(char *name)
{
	struct event *e;
	list_for(events, e) {
		if(streq(e->name, name))
			return e;
	}
	return null;
}
